from threading import Thread
import socket


class ClientEchoThread(Thread):
    def __init__(self, client):
        super().__init__()
        self.client = client

    def run(self):
        try:
            while True:
                data = self.client.recv(1024)
                # 如果没有数据，则引发异常，（连接被客户端关闭时，会出现这种情况）
                if not data:
                    raise BrokenPipeError("连接关闭")
                print(f"接收到数据{data}")
                self.client.sendall(data)
        # 当我们遇到异常时，退出run方法，这将终止线程
        except OSError as e:
            print(f"线程被终止:{e},关闭！")

    def close(self):
        if self.is_alive():
            # 如果线程处于活动状态，则关闭连接；
            # 如果客户端关闭连接，线程可能处于不活动状态
            self.client.sendall(bytes("关闭", encoding='utf-8'))
            # 关闭客户端连接以进行读取和写入
            self.client.shutdown(socket.SHUT_RDWR)


with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind(('127.0.0.1', 8080))
    server.listen()
    conn_threads = []
    try:
        while True:
            # 阻塞，等待客户端连接
            client, address = server.accept()
            # 一旦客户端完成连接，创建一个线程来运行eaho函数
            thread = ClientEchoThread(client)
            conn_threads.append(thread)
            # 开始运行线程
            thread.start()
    except KeyboardInterrupt:
        print("关闭")
        # 在我们的线程上调用close方法，以在键盘发生中断时关闭每个客户端连接
        [thread.close() for thread in conn_threads]
